/**
 * @file camera.cpp
 * @author riverflows2333 (liuyuzheng0801@163.com) axi404 (3406402603@qq.com)
 * @brief 相机类实现，传统方法
 * @version 1.0
 * @date 2022-12-25
 *
 * @copyright Copyright (c) 2022
 */

/*
 * 为一种格式写一个注释，这种格式在下方会反复出现，以下代码用于当语句没有正确执行时的报错，在正常的代码浏览中不再注释：
 * if(nRet != MV_OK)//如果函数返回
 * {
 *     LOG(ERROR) << "SetExposureTime ERROR:" << nRet;
 *     nRetTotal += 1;
 * }
 * 同时以LOG开头的语句为日志指令，亦不进行注释
 */

#include "camera.hpp"

Camera::Camera(GlobalParam &gp)
{
    // 对于Camera中的变量进行初始化
    this->nRet = MV_OK;
    this->handle = NULL;
    this->color = gp.color;
    this->attack_mode = ARMOR;
    this->pDataForRGB = (unsigned char *)malloc(1440 * 1080 * 4 + 2048);
    this->CvtParam = {0};
    this->stOutFrame = {0};
    this->frame_rate = {0};
    this->switch_INFO = gp.switch_INFO;
    memset(&this->stOutFrame, 0, sizeof(MV_FRAME_OUT));
    memset(&this->stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST));
    LOG_IF(INFO, this->switch_INFO == ON) << "Camera Initialization Successful.";

    // 初始化变量统计该方法总的错误数量
    int nRetTotal = 0;
    // 读取设备列表，储存入stDeviceList
    this->nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE, &stDeviceList);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "EnumDevices ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 依照gp中的cam_index，设置句柄为设备列表中第cam_index个设备(存在第0个)
    this->nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[gp.cam_index]);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "CreateHandle ERROR:" << this->nRet;
        nRetTotal += 1;
    }

    // 开启设备
    this->nRet = MV_CC_OpenDevice(handle);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "OpenDevice ERROR:" << this->nRet;
        nRetTotal += 1;
    }

    if (nRetTotal == 0)
    {
        LOG_IF(INFO, this->switch_INFO == ON) << "camera Successful";
    }
    else if (nRetTotal != 0)
    {
        LOG(ERROR) << "camera Total ERROR:" << nRetTotal;
    }
    printf("初始化完毕，欢迎使用喵～\n");
}

Camera::~Camera()
{
    // 初始化变量统计该方法总的错误数量
    int nRetTotal = 0;
    // 停止取流
    this->nRet = MV_CC_StopGrabbing(this->handle);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "StopGrabbing ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 关闭设备
    this->nRet = MV_CC_CloseDevice(this->handle);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "CloseDevice ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 销毁句柄
    this->nRet = MV_CC_DestroyHandle(this->handle);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "DestroyHandle ERROR:" << this->nRet;
        nRetTotal += 1;
    }

    if (nRetTotal == 0)
    {
        LOG_IF(INFO, this->switch_INFO == ON) << "~camera Successful";
    }
    else if (nRetTotal != 0)
    {
        LOG(ERROR) << "~camera Total ERROR:" << nRetTotal;
    }
    printf("析构中，再见喵～\n");
    // 剩余内容系统自动释放
}

int Camera::start_camera(GlobalParam &gp)
{
    // 初始化变量统计该方法总的错误数量
    int nRetTotal = 0;
    // 开始取流
    this->nRet = MV_CC_StartGrabbing(this->handle);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "StartGrabbing ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    if (nRetTotal == 0)
    {
        LOG_IF(INFO, this->switch_INFO == ON) << "start_camera Successful";
    }
    else if (nRetTotal != 0)
    {
        LOG(ERROR) << "start_camera Total ERROR:" << nRetTotal;
    }
    return nRetTotal;
}

int Camera::get_pic(cv::Mat *srcimg, GlobalParam &gp)
{
    // 初始化变量统计该方法总的错误数量
    int nRetTotal = 0;
    // 获取图像储存在stOutFrame中，等待超时时间为400Msec
    this->nRet = MV_CC_GetImageBuffer(handle, &stOutFrame, 400);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "GetImageBuffer ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置CvtParam参数列表中的一些内容，这些内容将在ConvertPixelType中使用。
    CvtParam.enSrcPixelType = stOutFrame.stFrameInfo.enPixelType;
    CvtParam.enDstPixelType = PixelType_Gvsp_RGB8_Packed;
    CvtParam.nHeight = stOutFrame.stFrameInfo.nHeight;
    CvtParam.nWidth = stOutFrame.stFrameInfo.nWidth;
    CvtParam.nDstBufferSize = stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 4 + 2048;
    CvtParam.pSrcData = stOutFrame.pBufAddr;
    CvtParam.pDstBuffer = pDataForRGB;
    CvtParam.nSrcDataLen = stOutFrame.stFrameInfo.nFrameLen;
    // 该函数可以将从设备采集到的原始图像数据转换成用户所需的像素格式并存放在指定内存中，此处需要的全部信息均在CvtParam中，之后会以RGB的格式被存放在pDataForRGB中
    this->nRet = MV_CC_ConvertPixelType(handle, &CvtParam);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "ConvertPixelType ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 用输入的一张Mat读取pDataForRGB中的图像信息
    *srcimg = Mat(stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nWidth, CV_8UC3, pDataForRGB);
    // 将srcimg由RGB转化为BGR图片
    cvtColor(*srcimg, *srcimg, COLOR_RGB2BGR); // COLOR_RGB2BGR  COLOR_BayerRG2BGR COLOR_BayerRG2BGR
    // 如果成功读取了，stOutFrame.pBufAddr不为空，将其清空缓存，等待下一次的getImageBuffer读取
    if (NULL != stOutFrame.pBufAddr)
    {
        this->nRet = MV_CC_FreeImageBuffer(handle, &stOutFrame);
        if (this->nRet != MV_OK)
        {
            LOG(ERROR) << "FreeImageBuffer ERROR:" << this->nRet;
            nRetTotal += 1;
        }
    }
    if (nRetTotal == 0)
    {
        LOG_IF(INFO, this->switch_INFO == ON) << "get_pic Successful";
    }
    else if (nRetTotal != 0)
    {
        LOG(ERROR) << "get_pic Total ERROR:" << nRetTotal;
    }
    return nRetTotal;
}

int Camera::set_param_once(GlobalParam &gp)
{
    // 初始化变量统计该方法总的错误数量
    int nRetTotal = 0;
    // 设置触发模式
    this->nRet = MV_CC_SetTriggerMode(this->handle, gp.enable_trigger);
    if (nRet != MV_OK)
    {
        LOG(ERROR) << "SetTriggerMode ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置自动曝光使能
    nRet = MV_CC_SetExposureAutoMode(this->handle, gp.enable_auto_exp);
    if (nRet != MV_OK)
    {
        LOG(ERROR) << "SetExposureAutoMode ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置曝光时间，默认为蓝方击打装甲板的情况
    this->nRet = MV_CC_SetExposureTime(this->handle, gp.armor_exp_time);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetExposureTime ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置自动增益使能
    this->nRet = MV_CC_SetGainMode(this->handle, gp.enable_auto_gain);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetGainMode ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置增益值
    this->nRet = MV_CC_SetGain(this->handle, gp.gain);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetGain ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置图像格式为Bayer RG8
    this->nRet = MV_CC_SetPixelFormat(this->handle, gp.pixel_format);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetPixelFormat ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置gamma值，因未知问题无法使用，不过不影响取流实现
    this->nRet = MV_CC_SetGamma(this->handle, gp.gamma_value);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetGamma ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置白平衡，依次顺序为BGR，默认为蓝方
    this->nRet = MV_CC_SetBalanceRatioBlue(this->handle, gp.b_balance);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetBalanceRatioBlue ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    this->nRet = MV_CC_SetBalanceRatioGreen(this->handle, gp.g_balance);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetBalanceRatioGreen ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    this->nRet = MV_CC_SetBalanceRatioRed(this->handle, gp.r_balance);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetBalanceRatioRed ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 设置帧率
    this->nRet = MV_CC_SetFrameRate(this->handle, gp.frame_rate);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetFrameRate ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    if (nRetTotal == 0)
    {
        LOG_IF(INFO, this->switch_INFO == ON) << "set_param_once Successful";
    }
    else if (nRetTotal != 0)
    {
        LOG(ERROR) << "set_param_once Total ERROR:" << nRetTotal;
    }
    return nRetTotal;
}

int Camera::set_param_mult(GlobalParam &gp)
{
    // 初始化变量统计该方法总的错误数量
    int nRetTotal = 0;
    // 设置白平衡，依次顺序为BGR
    this->nRet = MV_CC_SetBalanceRatioBlue(this->handle, gp.b_balance);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetBalanceRatioBlue ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    this->nRet = MV_CC_SetBalanceRatioGreen(this->handle, gp.g_balance);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetBalanceRatioGreen ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    this->nRet = MV_CC_SetBalanceRatioRed(this->handle, gp.r_balance);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "SetBalanceRatioRed ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    // 如果当前状态为打击能量机关，输入打红色能量机关对应的曝光时间
    if (this->attack_mode == ENERGY)
    {
        this->nRet = MV_CC_SetExposureTime(this->handle, gp.energy_exp_time);
        if (this->nRet != MV_OK)
        {
            LOG(ERROR) << "SetExposureTime ERROR:" << this->nRet;
            nRetTotal += 1;
        }
    }
    // 如果当前状态为打击装甲板，输入打红色装甲板对应的曝光时间
    else if (this->attack_mode == ARMOR)
    {
        this->nRet = MV_CC_SetExposureTime(this->handle, gp.armor_exp_time);
        if (this->nRet != MV_OK)
        {
            LOG(ERROR) << "SetExposureTime ERROR:" << this->nRet;
            nRetTotal += 1;
        }
    }
    if (nRetTotal == 0)
    {
        LOG_IF(INFO, this->switch_INFO == ON) << "set_param_mult Successful";
    }
    else if (nRetTotal != 0)
    {
        LOG(ERROR) << "set_param_mult Total ERROR:" << nRetTotal;
    }
    return nRetTotal;
}

int Camera::change_color(int input_color, GlobalParam &gp)
{
    this->color = input_color;
    LOG_IF(INFO, this->switch_INFO == ON) << "Change Color Success";
    return 0;
}

int Camera::change_attack_mode(int input_attack_mode, GlobalParam &gp)
{
    this->attack_mode = input_attack_mode;
    LOG_IF(INFO, this->switch_INFO == ON) << "Change Attack Mode Success";
    return 0;
}

int Camera::request_frame_rate(GlobalParam &gp)
{
    // 初始化变量统计该方法总的错误数量
    int nRetTotal = 0;
    // 读取帧率信息，用于调试
    this->nRet = MV_CC_GetFrameRate(handle, &frame_rate);
    if (this->nRet != MV_OK)
    {
        LOG(ERROR) << "GetFrameRate ERROR:" << this->nRet;
        nRetTotal += 1;
    }
    LOG_IF(INFO, this->switch_INFO == ON) << "Request Frame Rate Success, Current Frame Rate: " << frame_rate.fCurValue;
    // 打印帧率信息，用于调试
    printf("当前帧率:%f\n", frame_rate.fCurValue);
    return nRetTotal;
}